#include "window.hpp"
#include "window_callbacks.hpp"

#include "core/assert.hpp"
#include "core/log.hpp"

namespace {
    void glfw_error_callback(int code, const char *msg)
    {
        LOG_W("GLFW ERROR: %s(%d)", msg, code);
    }
}  // namespace

namespace core::app {
    Window::Window(u16 width, u16 height, const char *title) :
        mainWnd {nullptr}, exec_code {}, app {}
    {
        InitGLFW();
        InitWindow(width, height, title);
    }

    Window::~Window()
    {
        DestroyWindow();
        TerminateGLFW();
    }

    void Window::InitGLFW()
    {
        glfwSetErrorCallback(&glfw_error_callback);
        int code = glfwInit();
        CORE_ASSERT_MSG(code == GLFW_TRUE, "GLFW Init Failed!");
    }

    void Window::InitWindow(u16 width, u16 height, const char *title)
    {
        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
        mainWnd = glfwCreateWindow(width, height, title, nullptr, nullptr);
        // glfwSetInputMode(mainWnd, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
        CORE_ASSERT_MSG(mainWnd, "Window Creation Failed!");
        glfwSetWindowUserPointer(mainWnd, this);

        WindowCallbacks::AttachCallbacks(mainWnd);
        m_input.Init(mainWnd);
    }

    void Window::DestroyWindow()
    {
        glfwDestroyWindow(mainWnd);
        mainWnd = nullptr;
    }

    void Window::TerminateGLFW() { glfwTerminate(); }

    void Window::SetApp(VulkanApp *vulkanApp) { this->app = vulkanApp; }

    int Window::AppRun()
    {
        auto app_backup = app;

        if (app_backup)
            app_backup->AttachWindow(this);

        while (!glfwWindowShouldClose(mainWnd)) {
            glfwPollEvents();
            if (app_backup)
                app_backup->Update();
        }

        if (app_backup)
            app_backup->DetachWindow();

        return exec_code;
    }

    void Window::Quit(int code)
    {
        exec_code = code;
        glfwSetWindowShouldClose(mainWnd, GLFW_TRUE);
    }

    GLFWwindow *Window::WindowHandle() const { return mainWnd; }

    const input::InputContext *Window::GetInput() const { return &m_input; }
}  // namespace core::app
